#include"myshell.h"
#include<iostream>
#include<cstdio>
#include<string.h>
#include<stdlib.h>
#include<string>
#include<cstring>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<fcntl.h>

//定义全局的命令行参数列表
char* gargv[ARGS] = {NULL};
int gargc =0;
//一般使用全局变量空间来保存当前shell 进程的工作路径
char pwd[4096];
int lastcode = 0;//记录最近程序结束的退出码

//支持重定向功能
#define NONE_REDIR 0
#define OUTPUT_REDIR 1
#define INPUT_REDIR 2
#define APPEND_REDIR 4

int redir_type = NONE_REDIR;//默认设置为没有重定向
std::string filename;//记录文件名

void InitGlobal()
{
   //置空gargv
   gargc = 0;
   memset(gargv , 0 , sizeof(gargv));
   redir_type  = NONE_REDIR;
   filename.clear();
}
static std::string GetUserName()
{
    //从环境变量中获取
    std::string username = getenv("USER");
    return username.empty() ? "None" : username;
}
static std::string GetHostName()
{
    std::string hostname = getenv("HOSTNAME");
    return hostname.empty() ? "None" : hostname;
}

static std::string GetPwd()
{
    //环境变量的变化可能会依赖于进程，pwd 需要shell 自己跟新环境变量中的PWD
    //此处可以使用系统调用 getcwd 来获取当前父进程自己的工作路径，那么子进程继承的时候也可以变向修改了
    char temp[1024];
    getcwd(temp , sizeof(temp));
    //使用putenv ，可以更新环境变量，但是putenv 的参数必须是KV结构
    //可以借助于snprintf 来实现格式化输出到字符串中
    snprintf(pwd ,sizeof(pwd) , "PWD=%s", temp);
    putenv(pwd);
    //std::string pwd = getenv("PWD");
    //return pwd.empty() ? "None" : pwd;
    //rfind + substr

    //可能是根目录，对于最后的结果需要判断
    std::string pwd_lable = temp;
    const std::string pathsep = "/";
    auto pos = pwd_lable.rfind(pathsep);
    if(pos == std::string::npos)
    {
        return "None";
    }
    pwd_lable = pwd_lable.substr(pos + pathsep.size());

    return pwd_lable.empty() ? "/" : pwd_lable;
}

static std::string GetHomePath()
{
    std::string home = getenv("HOME");
    return home.empty() ? "/" : home;
}

//输出命令行提示符
void PrintCommandPrompt()
{
    //printf("hello\n");
    std::string user = GetUserName();
    std::string hostname = GetHostName();
    std::string pwd = GetPwd();
    //std::cout << user << " " << pwd << std::endl;
    printf("[%s@%s %s]# " , user.c_str(), hostname.c_str() , pwd.c_str());
}

//获取用户键盘输入
bool GetCommandString(char cmd_str_buffer[] , int len)
{
    if(cmd_str_buffer==NULL || len <=0) return false;
    char* res = fgets(cmd_str_buffer , len , stdin);
    if(res==NULL) return false;
    //cmd_str_buffer 中一定会有最后我们输入命令按下的enter ,需要对其进行处理
    //并且判断是否空串，如果为空串就返回false
    cmd_str_buffer[strlen(cmd_str_buffer)-1]=0;
    return strlen(cmd_str_buffer) == 0 ? false : true;
}

//对用户输入的字符串做解析
bool ParseCommandString(char cmd[])
{
    if(cmd == NULL) return false;
//函数内部可以定义宏
#define SEP " "
    //利用strtok 进行分割，第一次传cmd , 第二次传NULL， strtok 内部会自动进行定位
    //当strtok 读完的时候会返回0
    gargv[gargc++] = strtok(cmd,SEP);
    while((bool)(gargv[gargc++] = strtok(NULL,SEP)));
    //当strtok 返回0也会让gargc 自增一次，所以需要减
    gargc--;

    //增加利用条件编译写的Debug 
//#define DEBUG
#ifdef DEBUG
    printf("gargc:%d\n" , gargc);
    for(int i = 0;i<gargc;i++)
    {
        printf("gargv[%d]: %s\n" , i , gargv[i]);
    }
    printf("-------------------\n");
    for(int i = 0;gargv[i];i++)
    {
        printf("gargv[%d] : %s\n", i , gargv[i]);
    }
#endif

    return true;
}

void ForkAndExec()
{
    //创建子进程，子进程进行程序替换，父进程等待子进程
    pid_t id = fork();
    if(id <0)
    {
        perror("fork");
        return;
    }
    else if(id == 0)
    {
        //在让子进程执行前，判断是否需要重定向
        //如果需要重定向，使用dup2 来更换文件描述符所指向的文件
        if(redir_type == OUTPUT_REDIR)
        {
            //首先需要打开文件
            int fd = open(filename.c_str() , O_CREAT | O_TRUNC | O_WRONLY, 0666);
            if(fd < 0)
            {
                perror("open");
                exit(2);
            }
            //输出重定向
            dup2(fd , 1);
        }
        else if(redir_type == INPUT_REDIR)
        {
            int fd = open(filename.c_str() , O_RDONLY);
            if(fd < 0)
            {
                perror("open");
                exit(2);
            }
            //输入重定向
            dup2(fd , 0);
        }
        else if(redir_type == APPEND_REDIR)
        {
            int fd = open(filename.c_str() , O_CREAT | O_WRONLY | O_APPEND , 0666);
            if(fd < 0)
            {
                perror("open");
                exit(2);
            }
            dup2(fd , 1);
        }
        else
        {
            
        }

       //子进程，使用execvp
       execvp(gargv[0], gargv);
       exit(0);
    }
    else
    {
        int status = 0;
        //父进程等待子进程
        pid_t rid = waitpid(id , &status , 0);
        if(rid > 0)
        {
            lastcode = WEXITSTATUS(status);
        }
    }
}

//判断命令是否为内建命令
bool BuiltInCommandExec()
{
    //内建命令不多，可以直接进行枚举, 判断gargv[0] 是否为内建命令便可
    std::string cmd = gargv[0];
    bool ret = false;//作为返回值
    if(cmd == "cd")
    {
        //一个参数、两个参数
        if(gargc == 2)
        {
            //特殊处理，eg. cd -、cd ~
            std::string target = gargv[1];
            if(target == "~")
            {
                ret = true;
                //回到家目录
                chdir(GetHomePath().c_str());
            }
            else
            {
                ret = true;
                //使用系统调用chdir 来切换当前工作路径
                chdir(gargv[1]);
            }
        }
        else if(gargc == 1)
        {
            ret = true;
           //回到家目录
           chdir(GetHomePath().c_str());
        }
        else
        {
            //bug
        }
    }
    else if(cmd == "echo")
    {
        if(gargc == 2)
        {
            std::string args = gargv[1];
            if(args[0] == '$')
            {
                if(args[1]=='?')
                {
                    printf("lastcode:%d\n" , lastcode);
                    lastcode = 0;
                    ret = true;
                }
                else
                {
                    const char* name = &args[1];
                    printf("%s\n", getenv(name));
                    lastcode = 0;
                   ret = true;
                }
            }
            else
            {
                printf("%s\n" , args.c_str());
                ret = true;
            }
        }
    }
    return ret;
}

#define TrimSpace(start) do{\
        while(isspace(*start))\
         {\
             ++start;\
         }\
}while(0)

void CheckCommand(char cmd[])
{
    //遍历字符串看是否存在重定向符号
    char* start = cmd;
    char* end = start + strlen(cmd)-1;

    while(start <= end)
    {
        if(*start == '>')
        {
            if(*(start+1) == '>') 
            {
                //追加重定向
                redir_type = APPEND_REDIR;
                *start = '\0';
                start+=2;
                //去除空格
                TrimSpace(start);
                filename = start;
                break;
            }
            else
            {
                //输出重定向
                redir_type  = OUTPUT_REDIR;
                *start = '\0';
                start++;
                TrimSpace(start);
                filename = start;
                break;
            }
        }
        else if(*start == '<')
        {
            //输入重定向
            redir_type = INPUT_REDIR;
            *start = '\0';
            start++;
            TrimSpace(start);
            filename = start;
            break;
        }
        else
        {
            start++;
        }
    }
}
